﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using CCWin; //UI

using System.IO; //文件处理
using System.Threading; //创建多线程使用
using System.IO.Ports; //查看电脑端口

using System.Text.RegularExpressions;

using ZLGCAN;
using System.Runtime.InteropServices;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar;
using System.Runtime.Remoting.Channels;
using CCWin.SkinClass;
using System.Security.Cryptography;

namespace Ymodem_tool
{


    public partial class mcfsFileDownload : Skin_Mac
    {
        #region 窗体初始化
        public mcfsFileDownload()
        {
            InitializeComponent();
            BaudComboBoxInit(customCombox_Baud);
            ScanAvailableSerialPort();
        }

        //窗体加载
        private void YmodemFileDownload_Load(object sender, EventArgs e)
        {
            this.MaximizeBox = false; //禁用最大化按钮
        }

        //波特率下拉框的选项初始化
        public void BaudComboBoxInit(ComboBox comboBox)
        {
            //设置下拉选项样式，只能从下列选择，不能自已输入
            comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
            //添加元素
            comboBox.Items.Add("9600");
            comboBox.Items.Add("19200");
            comboBox.Items.Add("38400");
            comboBox.Items.Add("115200");
            comboBox.Items.Add("128000");
            comboBox.Items.Add("230400");
            comboBox.Items.Add("256000");
            comboBox.Items.Add("460800");
            comboBox.Items.Add("512000");
            comboBox.Items.Add("921600");
            comboBox.Items.Add("1000000");
            comboBox.Items.Add("2000000");
            comboBox.Items.Add("5000000");
            comboBox.Items.Add("10000000");
            comboBox.Items.Add("12000000");
            comboBox.Items.Add("15000000");
            comboBox.Items.Add("20000000");
            //选择默认选择的元素
            comboBox.SelectedIndex = 0;
        }
        #endregion

        #region 串口处理
        //扫描可用串口
        private void button_UartPortScan_Click(object sender, EventArgs e)
        {
            ScanAvailableSerialPort();
        }

        /// <summary>
        /// 获取当前所有串口信息
        /// </summary>
        private void ScanAvailableSerialPort()
        {
            SerialTransmission Serial = new SerialTransmission();
            string currentPort = PORT.Text; //保存当前选择的串口号
            Serial.GetSerialPort(PORT);
            PORT.Text = currentPort;
        }

        //获取串口号名 (如COM1)
        private string GetSerialPortName(string portText)
        {
            Regex reg = new Regex(@"COM\d+");
            Match matchs = reg.Match(portText);
            if (matchs != null && matchs.Groups[0].ToString() != string.Empty)
            {
                Debugging.output("port_name = " + matchs.Groups[0].ToString());
                return matchs.Groups[0].ToString();
            }
            else
            {
                Debugging.output("无法识别的串口。");
            }

            return "";
        }
        #endregion

        #region 升级文件数据提取
        private string _updatePath = ""; //升级文件路径
        private bool _loadUpdateFileStatus = false; //加载升级文件状态 (true,加载成功 / false,未加载)

        private void skinButton_OpenUpdateFile_Click(object sender, EventArgs e)
        {
            //创建打开文件对话框对象
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "|*.bin"; //只查看bin格式文件
            if (ofd.ShowDialog() == DialogResult.OK)//判断是选中文件
            {
                FileInfo f = new FileInfo(ofd.FileName); //创建FileInfo对象
                ClearUpdateBinFileDisplay();
                if (mcfs.EnglishVerson == 1)
                {
                    AddUpdateBinFileDisplay("File name： " + ofd.FileName + "\r\n"); //文件名(即包含绝对路径的文件名)
                    AddUpdateBinFileDisplay("File length： " + f.Length.ToString() + " bytes\r\n"); //文件字节大小
                    AddUpdateBinFileDisplay("Creation date： " + f.CreationTime.ToString() + "\r\n"); //文件创建日期
                    AddUpdateBinFileDisplay("Last modified date： " + f.LastWriteTime.ToString()); //文件最后次修改日期
                }
                else {

                    AddUpdateBinFileDisplay("文件名： " + ofd.FileName + "\r\n"); //文件名(即包含绝对路径的文件名)
                    AddUpdateBinFileDisplay("文件长度： " + f.Length.ToString() + " 字节\r\n"); //文件字节大小
                    AddUpdateBinFileDisplay("文件创建日期： " + f.CreationTime.ToString() + "\r\n"); //文件创建日期
                    AddUpdateBinFileDisplay("文件最后修改时间： " + f.LastWriteTime.ToString()); //文件最后次修改日期

                }

                _loadUpdateFileStatus = true;

                _updatePath = ofd.FileName;
            }
            else
            {
                if (mcfs.EnglishVerson == 1)
                    AddUpdateBinFileDisplay("Error in adding the BIN file");
                else
                    AddUpdateBinFileDisplay("添加BIN文件出错");

                _loadUpdateFileStatus = false;
            }
        }
        #endregion

        #region 调试辅助信息显示
        //文件信息显示窗口清空显示
        private void ClearUpdateBinFileDisplay()
        {
            this.Invoke(new Action(() =>
            {
                richTextBox_UpdateBinFileData.Text = "";
            }));
        }

        //文件信息显示窗口添加内容显示
        private void AddUpdateBinFileDisplay(string info)
        {
            this.Invoke(new Action(() =>
            {
                richTextBox_UpdateBinFileData.AppendText(info);
            }));
        }

        private void ProcessInfoClear()
        {
            this.Invoke(new Action(() =>
            {
                richTextBox_UoloadResultDisplay.Clear();
            }));
        }

        private void ProcessInfoAdd(string text)
        {
            this.Invoke(new Action(() =>
            {
                richTextBox_UoloadResultDisplay.Text += text + "\r\n";
            }));
        }
        #endregion

        #region 下载线程处理
        private Thread _fileDownloadThread;
        private mcfs ymodem = new mcfs();
        private YModemType _yModemType = YModemType.YModem_1K;
        private recvdatathread recv_data_thread_;

        const uint CAN_EFF_FLAG = 0x80000000U; /* EFF/SFF is set in the MSB */
        const uint CAN_RTR_FLAG = 0x40000000U; /* remote transmission request */
        const uint CAN_ERR_FLAG = 0x20000000U; /* error message frame */
        const uint CAN_ID_FLAG = 0x1FFFFFFFU; /* id */

        //停止文件下载
        private void FileDownloadStop()
        {
            try
            {
                Method.ZCAN_CloseDevice(ymodem.device_handle_);
            }
            catch
            {
            }
        }

        //canbuf
        private bool FileDownloadProc()
        {
            //CanSendData
            ProcessInfoClear();
            this.Invoke(new Action(() =>
            {
                this.progressBar_Upload.Value = 0;
                this.label_UploadProgress.Text = "0 %";
            }));

            //开始下载
            if (mcfs.EnglishVerson == 1)
                ProcessInfoAdd("Downloading...");
            else
                ProcessInfoAdd("开始下载...");
            //ymodem = new Ymodem();
            ymodem.path = _updatePath;
            ymodem.NowDownloadProgressEvent += new EventHandler(NowDownloadProgressEvent);
            ymodem.DownloadResultEvent += new EventHandler(DownloadFinishEvent);
            ymodem.YmodemUploadFile(_yModemType, 0);

            return true;
        }

        //下载线程
        private void FileDownloadThreadProc()
        {
            //中途停止下载
            if (_fileDownloadThread != null && _fileDownloadThread.IsAlive)
            {
                ymodem.YmodemUploadFileStop();
                return;
            }

            //开始下载
            if (mcfs.EnglishVerson == 1)
                this.skinButton_FileDownload_StartDownload.Text = "Cancel";
            else
                this.skinButton_FileDownload_StartDownload.Text = "取消下载";
            this.skinButton_FileDownload_StartDownload.BackgroundImage = mcfs_tool.Properties.Resources.Red_button_64px;
            _fileDownloadThread = new Thread(() =>
            {
                FileDownloadProc();
            });
            _fileDownloadThread.IsBackground = true; //设置为后台进程,关闭当前窗体或线程运行结束就会结束该线程
            _fileDownloadThread.Start();
        }

        //开始下载升级
        private void skinButton_FileDownload_StartDownload_Click(object sender, EventArgs e)
        {
            richTextBox_UoloadResultDisplay.Clear();

            //确认设备已连接
            if (ymodem.m_bOpen == false)
            {
                if (mcfs.EnglishVerson == 1)
                    MessageBox.Show("Please connect the device first！");
                else
                    MessageBox.Show("请先连接设备！");
                return;
            }

            //确认升级文件加载合法性
            if (_loadUpdateFileStatus == false)
            {
                if (mcfs.EnglishVerson == 1)
                    MessageBox.Show("Please load the upgrade file first！");
                else
                    MessageBox.Show("请先加载升级文件！");
                return;
            }

            //下载流程
            FileDownloadThreadProc();
        }
        #endregion

        #region 下载进度委托及事件响应
        private void NowDownloadProgressEvent(object sender, EventArgs e)
        {
            int value = Convert.ToInt32(sender);
            this.Invoke(new Action(() =>
            {
                this.progressBar_Upload.Value = value;
                this.label_UploadProgress.Text = value.ToString() + " %";
            }));
        }
        #endregion

        #region 下载完成委托及事件响应
        private delegate void DownloadFinish(bool finish);

        private void DownloadFinishEvent(object sender, EventArgs e)
        {
            bool finish = (Boolean)sender;
            DownloadFinish status = new DownloadFinish(UploadFileResult);
            this.Invoke(status, finish);
        }

        private void UploadFileResult(bool result)
        {
            if (result == true)
            {
                if (mcfs.EnglishVerson == 1)
                    ProcessInfoAdd("Download success！");
                else
                    ProcessInfoAdd("下载成功！");
            }
            else
            {
                if (mcfs.EnglishVerson == 1)
                    ProcessInfoAdd("Download failed！");
                else
                    ProcessInfoAdd("下载失败！");
            }
            if (mcfs.EnglishVerson == 1)
                this.skinButton_FileDownload_StartDownload.Text = "Start downloading";
            else
                this.skinButton_FileDownload_StartDownload.Text = "开始下载";

            this.skinButton_FileDownload_StartDownload.BackgroundImage = mcfs_tool.Properties.Resources.Light_blue_button_64px;
        }

        #endregion

        public bool IsEFF(uint id)//1:extend frame 0:standard frame
        {
            return !!Convert.ToBoolean((id & CAN_EFF_FLAG));
        }

        public bool IsRTR(uint id)//1:remote frame 0:data frame
        {
            return !!Convert.ToBoolean((id & CAN_RTR_FLAG));
        }

        public bool IsERR(uint id)//1:error frame 0:normal frame
        {
            return !!Convert.ToBoolean((id & CAN_ERR_FLAG));
        }

        public uint GetId(uint id)
        {
            return id & CAN_ID_FLAG;
        }

        private void AddData(ZCAN_Receive_Data[] data, uint len)
        {
            //string text = "";
            for (uint i = 0; i < len; ++i)
            {
                ZCAN_Receive_Data can = data[i];
                //uint id = data[i].frame.can_id;
                //string eff = IsEFF(id) ? "扩展帧" : "标准帧";
                //string rtr = IsRTR(id) ? "远程帧" : "数据帧";
                //text = String.Format("接收到CAN ID:0x{0:X8} {1:G} {2:G} 长度:{3:D1} 数据:", GetId(id), eff, rtr, can.frame.can_dlc);

                ymodem.buflock = true;
                for (uint j = 0; j < can.frame.can_dlc; ++j)
                {
                    //text = String.Format("{0:G}{1:X2}[{2:D4}][{3:D4}] ", text, can.frame.data[j], ymodem.bb, ymodem.bt);
                    ymodem.canbuf[ymodem.bb++] = can.frame.data[j];
                    if (ymodem.bb == 4096) ymodem.bb = 0; 
                }
                ymodem.buflock = false;
                //ProcessInfoAdd(text);
            }
        }

        private void button_Connect_Click(object sender, EventArgs e)
        {
            int baud = 0;
            string portText = "";

            if (button_Connect.Text == "断开连接" || button_Connect.Text == "Disconnect")
            {
                if (radioButton.Checked)
                {
                    Method.ZCAN_CloseDevice(ymodem.device_handle_);
                }
                else
                {
                    ymodem.serialPort.Close();
                }
                if (mcfs.EnglishVerson == 1)
                    this.button_Connect.Text = "Connect Device";
                else
                    this.button_Connect.Text = "连接设备";
                ymodem.m_bOpen = false;
            }
            else
            {
                if (radioButton.Checked)
                {

                    ymodem.device_handle_ = Method.ZCAN_OpenDevice(Define.ZCAN_USBCAN1, 0, 0);
                    if ((int)ymodem.device_handle_ == 0)
                    {

                        if (mcfs.EnglishVerson == 1)
                            MessageBox.Show("Failed to open the device, please check that the device type and device index number are correct", "Prompt",
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        else
                            MessageBox.Show("打开设备失败,请检查设备类型和设备索引号是否正确", "提示",
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                        return;
                    }
                    else
                    {
                        Method.ZCAN_SetValue(ymodem.device_handle_, "0/baud_rate", Encoding.ASCII.GetBytes("1000000"));
                        ZCAN_CHANNEL_INIT_CONFIG config_ = new ZCAN_CHANNEL_INIT_CONFIG();
                        config_.can_type = Define.TYPE_CAN;
                        config_.can.filter = 0;
                        config_.can.acc_code = 0;
                        config_.can.acc_mask = 0xFFFFFFFF;
                        config_.can.mode = 0;

                        IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config_));
                        Marshal.StructureToPtr(config_, pConfig, true);

                        ymodem.channel_handle_ = Method.ZCAN_InitCAN(ymodem.device_handle_, 0, pConfig);
                        if ((int)ymodem.channel_handle_ == 0)
                        {
                            if (mcfs.EnglishVerson == 1)
                                MessageBox.Show("Failed to initialize the CAN", "Prompt", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            else
                                MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            return;
                        }
                        Marshal.FreeHGlobal(pConfig);

                        if (Method.ZCAN_StartCAN(ymodem.channel_handle_) != Define.STATUS_OK)
                        {
                            if (mcfs.EnglishVerson == 1)
                                MessageBox.Show("Failed to start the CAN", "Prompt", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            else
                                MessageBox.Show("启动CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            return;
                        }
                        if (recv_data_thread_ == null)
                        {
                            recv_data_thread_ = new recvdatathread();
                            recv_data_thread_.setChannelHandle(ymodem.channel_handle_);
                            recv_data_thread_.setStart(true);
                            recv_data_thread_.RecvCANData += this.AddData;
                        }
                        else
                        {
                            recv_data_thread_.setChannelHandle(ymodem.channel_handle_);
                        }
                    }
                }
                else
                {
                    try
                    {
                        this.Invoke(new Action(() =>
                        {
                            portText = PORT.Text;
                            baud = Convert.ToInt32(customCombox_Baud.SelectedItem.ToString());
                        }));
                        ymodem.serialPort.PortName = GetSerialPortName(portText);
                        ymodem.serialPort.BaudRate = baud;
                        ymodem.serialPort.ReadTimeout = 10000;
                        ymodem.serialPort.Open();

                        Debugging.output("uart name=" + ymodem.serialPort.PortName);
                    }
                    catch
                    {
                        if (mcfs.EnglishVerson == 1)
                            ProcessInfoAdd("Failed to open the serial port of the device！");
                        else
                            ProcessInfoAdd("打开设备的串口失败！");
                    }
                }
                if(mcfs.EnglishVerson == 1)
                    this.button_Connect.Text = "Disconnect";
                else
                    this.button_Connect.Text = "断开连接";
                ymodem.m_bOpen = true;
            }
            ymodem.mbuf = radioButton1.Checked;
        }

        private void ID_ValueChanged(object sender, EventArgs e)
        {
            ymodem.cid = (uint)ID.Value;
        }
    }
}
